//时间段构造函数
function T(duration) {
  //时间段开始计时
  this.start = function () {
    this.startTime = new Date().getTime();
    return this;
  };
  //获取已耗时间与总时间的比值
  this.getT = function () {
    return (new Date().getTime() - this.startTime) / duration;
  };
}
//获取一个坐标点对象
function p(x, y, z) {
  return {
    x: x || 0,
    y: y || 0,
    z: z || 0
  };
}
//获取某时刻一阶贝塞尔曲线所在的坐标点
function b(t, p0, p1) {
  return {
    x: (1 - t) * p0.x + t * p1.x,
    y: (1 - t) * p0.y + t * p1.y,
    z: (1 - t) * p0.z + t * p1.z
  };
}


//获取一个随机的HEX颜色
function getRandColor() {
  return '#' + (Math.floor(Math.random() * 0x1000000) + 0x1000000).toString(16).slice(1);
}
//画点
function dot(ctx, p) {
  ctx.fillStyle = '#f00';
  ctx.beginPath();
  ctx.arc(p.x, p.y, 3, 0, 2 * Math.PI);
  ctx.fill();
  ctx.closePath();
}
//画线
function line(ctx, p0, p1, color) {
  ctx.strokeStyle = color || '#00f';
  ctx.beginPath();
  ctx.moveTo(p0.x, p0.y);
  ctx.lineTo(p1.x, p1.y);
  ctx.stroke();
  ctx.closePath();
}
//写字
function text(ctx, txt, p, offset) {
  ctx.fillStyle = '#000';
  offset = offset || { x: 0, y: 0 };
  ctx.font = "30px Arial";
  ctx.fillText(txt, p.x + offset.x, p.y + offset.y);
}


//用线连接点集合里面的点
function join(ctx, points, color) {
  for (var i = 0; i < points.length - 1; i++) {
    line(ctx, points[i], points[i + 1], color);
  }
}
//画点集合里面的所有的点
function dots(ctx, points) {
  points.forEach(function (p) { dot(ctx, p); });
}
//为点集合里面所有点贴上标签
function label(ctx, points) {
  points.forEach(function (p, i, ps) { text(ctx, 'P' + i, p, { x: -20, y: p.y >= ps[0].y ? 32 : -5 }); });
}


//n阶贝塞尔曲线原理动画引擎
function bezier(bg, lines, curve, points, duration, flag) {//flag：是否显示绘制原理
  curve.clearRect(0, 0, curve.canvas.width, curve.canvas.height);//清空曲线canvas
  //背景canvas部分
  bg.clearRect(0, 0, bg.canvas.width, bg.canvas.height);//清空背景canvas
  bg.fillStyle = '#00f';
  bg.font = "40px Arial";
  bg.fillText((points.length - 1) + ' 阶贝塞尔曲线原理', 125, 50);
  if (flag) {
    join(bg, points);//在背景canvas上线连接points所有的点
    dots(bg, points);//在背景canvas上画points所有的点
    label(bg, points);//在背景canvas上为points所有点贴上标签
  }

  //直线canvas和曲线canvas部分
  var t = new T(duration).start();
  var start = points[0], end, tRate;
  var loop = function () {
    lines.clearRect(0, 0, lines.canvas.width, lines.canvas.height);//清空直线canvas
    tRate = t.getT();//获取当前时间占比
    if (tRate <= 1) {
      var draw = function (lines, curve, points) {
        if (points.length > 1) {
          for (var i = 0, ps = []; i < points.length - 1; i++) {
            ps.push(b(tRate, points[i], points[i + 1]));
          }
          if (flag) {
            join(lines, ps, getRandColor());//在直线canvas上线连接ps所有的点
            dots(lines, ps);//在直线canvas上画ps所有的点
          }
          draw(lines, curve, ps);//递归调用draw方法
        } else if (points.length == 1) {
          line(curve, start, start = points[0], '#f00');//在曲线canvas上画极短的一条线段
          // dots(lines, points);//在直线canvas上画points所有的点
        }
      };
      draw(lines, curve, points);

      requestAnimationFrame(loop);
    }
  };
  loop();
}


/* 入口 */
//定义点集合
var ps = [],
  p1s = [p(100, 300), p(300, 100)],//一阶贝塞尔
  p2s = [p(100, 300), p(300, 100), p(500, 300)],//二阶贝塞尔
  p3s = [p(100, 300), p(200, 100), p(400, 300), p(500, 100)],//三阶贝塞尔
  p4s = [p(100, 300), p(200, 100), p(300, 300), p(400, 100), p(500, 300)],//四阶贝塞尔
  p5s = [p(100, 300), p(150, 180), p(260, 100), p(340, 100), p(450, 180), p(500, 300)],//五阶贝塞尔
  p6s = [p(100, 300), p(166, 100), p(232, 300), p(298, 100), p(366, 300), p(432, 100), p(500, 300)];//六阶贝塞尔

//获取绘图上下文
var bg = document.getElementById('bg').getContext('2d'),
  lines = document.getElementById('lines').getContext('2d'),
  curve = document.getElementById('curve').getContext('2d');

//启动贝塞尔曲线原理动画引擎
bezier(bg, lines, curve, p2s, 4000, true);
setTimeout(function () {
  bezier(bg, lines, curve, p3s, 4000, true);
  setTimeout(function () {
    bezier(bg, lines, curve, p4s, 4000, true);
    setTimeout(function () {
      bezier(bg, lines, curve, p5s, 4000, true);
      setTimeout(function () {
        bezier(bg, lines, curve, p6s, 4000, true);
        setTimeout(function () {
          let ps = Array(10).fill().map(()=>p(600 * Math.random(), 400 * Math.random()));
          bezier(bg, lines, curve, ps, 5000);
        }, 5000);
      }, 5000);
    }, 5000);
  }, 5000);
}, 5000);
